package com.niiwoo.civet.trade.service.local.auth;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.dangdang.ddframe.rdb.sharding.api.HintManager;
import com.dangdang.ddframe.rdb.sharding.hint.HintManagerHolder;
import com.niiwoo.civet.snapdata.enums.ProjectTypeRelationEnum;
import com.niiwoo.civet.snapdata.service.TianChengDataAnalysisDubboService;
import com.niiwoo.civet.trade.constant.LoanFlowMqConstant;
import com.niiwoo.civet.trade.dao.entity.Project;
import com.niiwoo.civet.trade.dao.entity.ProjectFlowExecute;
import com.niiwoo.civet.trade.dao.entity.ProjectFlowExecuteRuntime;
import com.niiwoo.civet.trade.dao.mapper.ProjectFlowExecuteMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectFlowExecuteRuntimeMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectMapperExt;
import com.niiwoo.civet.trade.dto.request.ProjectFlowRequestDTO;
import com.niiwoo.civet.trade.enums.*;
import com.niiwoo.civet.trade.service.dubbo.loan.SpeedLoanDubboServiceImpl;
import com.niiwoo.civet.trade.service.local.flowexecute.ProjectFlowExecuteService;
import com.niiwoo.civet.trade.service.local.loan.LoanReturnService;
import com.niiwoo.civet.trade.service.project.ProjectDubboService;
import com.niiwoo.civet.user.dto.response.UserBasicInfoResponseDTO;
import com.niiwoo.civet.user.dto.response.UserIdentityInfoResponseDTO;
import com.niiwoo.civet.user.enums.TianchengCallbackEventEnum;
import com.niiwoo.civet.user.service.UserBasicInfoDubboService;
import com.niiwoo.civet.user.service.UserIdentityDubboService;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import com.niiwoo.tripod.tiancheng.component.TianchengAgainstFakeService;
import com.niiwoo.tripod.tiancheng.enums.FunctionCodeEnum;
import com.niiwoo.tripod.tiancheng.enums.IsNeedDetailEnum;
import com.niiwoo.tripod.tiancheng.request.afp.RiskRuleRequest;
import com.niiwoo.tripod.tiancheng.response.common.RiskRuleResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.concurrent.CompletableFuture;

/**
 * @author wping
 * @Description: 12项风险信息规则服务类
 * @date 2018/4/23 11:05
 */
@Slf4j
@Service
public class RiskInfoRuleService {

    @Value(value = "${web-http-url}")
    private String webHttpUrl;

    // 执行次数阈值
    private static final int EXECUTE_TIMES_THRESHOLD = 2;

    @Autowired
    private ProjectFlowExecuteMapperExt projectFlowExecuteMapperExt;

    @Autowired
    private ProjectFlowExecuteRuntimeMapperExt projectFlowExecuteRuntimeMapperExt;

    @Autowired
    private ProjectFlowExecuteService projectFlowExecuteService;

    @Autowired
    private LoanReturnService loanReturnService;

    @Autowired
    private ProjectMapperExt projectMapperExt;

    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;

    @Reference(version = "1.0.0")
    private UserIdentityDubboService userIdentityDubboService;

    @Reference(version = "1.0.0")
    private UserBasicInfoDubboService userBasicInfoDubboService;

    @Autowired
    private TianchengAgainstFakeService tianchengAgainstFakeService;

    @Reference(version = "1.0.0")
    private TianChengDataAnalysisDubboService tianChengDataAnalysisDubboService;

    @Autowired
    private ProjectDubboService projectDubboService;

    public void handleRiskInfoRule(Long flowExecuteId) {

        String methodName = "12项风险信息规则处理, flowExecuteId:" + flowExecuteId;

        RiskInfoRuleService riskInfoRuleService = (RiskInfoRuleService) AopContext.currentProxy();
        if (!HintManagerHolder.isMasterRouteOnly()) {
            HintManagerHolder.clear();
            HintManager hintManager = HintManager.getInstance();
            hintManager.setMasterRouteOnly();
        }
        ProjectFlowExecute projectFlowExecute = projectFlowExecuteMapperExt.selectByPrimaryKey(flowExecuteId);
        if (projectFlowExecute == null || projectFlowExecute.getProjectType() == null) {
            log.error(methodName + "-失败，ProjectFlowExecute记录不存在或者projectType为空");
            return;
        }

        if (projectFlowExecute.getStatus() != null && ProjectFlowExecuteStatusEnum.EXECUTING.getStatus() != projectFlowExecute.getStatus().byteValue()) {
            log.info(methodName + "-流程已经结束，退出不更新");
            return;
        }

        if (!HintManagerHolder.isMasterRouteOnly()) {
            HintManagerHolder.clear();
            HintManager hintManager = HintManager.getInstance();
            hintManager.setMasterRouteOnly();
        }
        ProjectFlowExecuteRuntime projectFlowExecuteRuntime = projectFlowExecuteRuntimeMapperExt.selectByPrimaryKey(flowExecuteId);
        if (projectFlowExecuteRuntime == null) {
            log.error(methodName + "-失败，ProjectFlowExecuteRuntime记录不存在");
            riskInfoRuleService.failEndProjectFlowExecuteAndReturnProject(flowExecuteId, projectFlowExecute.getProjectId(), ReturnProjectReasonTypeEnum.OTHER_EXCEPTION, "ProjectFlowExecuteRuntime记录不存在");
            return;
        }

        if (LoanNodeTypeEnum.RISK_INFO_HANDLE.getId() != projectFlowExecuteRuntime.getNodeId().intValue()) {
            log.error(methodName + "-失败，当前节点并非12项风险规则处理，ProjectFlowExecuteRuntime.nodeId:{}", projectFlowExecuteRuntime.getNodeId());
            return;
        }

        int currentExecuteTimes = projectFlowExecuteRuntime.getRetryTimes() == null ? 1 : projectFlowExecuteRuntime.getRetryTimes() + 1;
        String userId = projectFlowExecute.getBorrowUserId();
        Long projectId = projectFlowExecute.getProjectId();
        methodName += ", userId:" + userId + ", projectId:" + projectId;
        // 判断标的状态
        if (!HintManagerHolder.isMasterRouteOnly()) {
            HintManagerHolder.clear();
            HintManager hintManager = HintManager.getInstance();
            hintManager.setMasterRouteOnly();
        }
        Project project = projectMapperExt.selectByPrimaryKey(projectId);
        if (project == null) {
            log.error(methodName + "-标的不存在");
            riskInfoRuleService.failEndProjectFlowExecute(flowExecuteId, "标的不存在");
            return;
        }

        if (!(project.getStatus().equals(ProjectStatusEnum.CHECKING.getStatus())
                && project.getSubStatus().equals(ProjectSubStatusEnum.CHECKING_INIT.getSubStatus()))) {
            log.error(methodName + "-标的状态不在审核中, 当前标的状态:{}", project.getStatus());
            riskInfoRuleService.failEndProjectFlowExecute(flowExecuteId, "标的状态不在审核中, 当前标的状态:" + project.getStatus());
            return;
        }

        try {
            UserIdentityInfoResponseDTO userIdentityInfoResponseDTO = userIdentityDubboService.get(userId);
            if (userIdentityInfoResponseDTO == null) {
                log.error(methodName + "-失败，原因是用户身份信息不存在");
                riskInfoRuleService.failEndProjectFlowExecuteAndReturnProject(flowExecuteId, projectFlowExecute.getProjectId(), ReturnProjectReasonTypeEnum.OTHER_EXCEPTION, "原因是用户身份信息不存在");
                return;
            }

            UserBasicInfoResponseDTO userBasicInfoResponseDTO = userBasicInfoDubboService.getUserBasicInfo(userId);
            if (userBasicInfoResponseDTO == null) {
                log.error(methodName + "-失败，原因是用户身份信息不存在");
                riskInfoRuleService.failEndProjectFlowExecuteAndReturnProject(flowExecuteId, projectFlowExecute.getProjectId(), ReturnProjectReasonTypeEnum.OTHER_EXCEPTION, "原因是用户基础信息不存在");
                return;
            }

            // 异常触发天秤数据分析
            tianChengDataAnalysis(flowExecuteId, userId, userBasicInfoResponseDTO.getMobileNo(), project.getType(), project.getFromSource());

            //触发天称12项风险规则;
            RiskRuleRequest riskRuleRequest = new RiskRuleRequest();
            riskRuleRequest.setTransDate(new Date());
            riskRuleRequest.setTransNo(String.valueOf(snowflakeIdWorker.nextId()));
            riskRuleRequest.setProjectId(String.valueOf(project.getProjectId()));
            riskRuleRequest.setUserId(String.valueOf(project.getBorrowUserId()));
            riskRuleRequest.setIdentityCard(userIdentityInfoResponseDTO.getIdCard());
            riskRuleRequest.setMobilePhone(userBasicInfoResponseDTO.getMobileNo());
            riskRuleRequest.setRealName(userIdentityInfoResponseDTO.getName());
            riskRuleRequest.setFunctionCode(FunctionCodeEnum.LIBRA_FRAUD_RISK_RULE.getBodyFunctionCode());
            riskRuleRequest.setIsNeedDetail(IsNeedDetailEnum.TRUE.getType());
            riskRuleRequest.setCallUrl(String.format(SpeedLoanDubboServiceImpl.TIANCHENG_AFP_CALLBACK_URL, webHttpUrl, TianchengCallbackEventEnum.SPEED_LOAN_RISK_RULE.getEventType(), project.getProjectId(), FunctionCodeEnum.LIBRA_FRAUD_RISK_RULE.getBodyFunctionCode()));
            riskRuleRequest.setVars(SpeedLoanDubboServiceImpl.getVars());
            ProjectFromSourceEnum projectFromSourceEnum = ProjectFromSourceEnum.enumOf(project.getFromSource());
            riskRuleRequest.setFromSource(TCRiskRuleFromSourceEnum.getTCRiskRuleFromSourceByProjectFromSource(projectFromSourceEnum));

            log.info(methodName + "-天称12项风险规则开始，请求参数:{}", JSON.toJSONString(riskRuleRequest));
            final String methodNameFin = methodName;
            CompletableFuture<RiskRuleResponse> completableFuture = tianchengAgainstFakeService.riskRule(riskRuleRequest);
            completableFuture.whenComplete((s, t) -> {
                // 成功
                if (s != null) {
                    RiskRuleResponse response = (RiskRuleResponse) s;
                    log.info(methodNameFin + "-天称12项风险规则返回报文:{}", JSON.toJSONString(response));
                }
                // 异常
                if (t != null) {
                    log.error(methodNameFin + "-天称十二项风险规则异常, 异常信息:{}", t);
                }
            });

            // 无论成功、失败，线程休眠60s后，执行流程下一步
            try {
                Thread.sleep(60000L);
                boolean isSuccess = projectFlowExecuteService.initNextStepProjectFlowExecute(flowExecuteId, LoanNodeTypeEnum.RISK_INFO_HANDLE, LoanNodeTypeEnum.LOAN_ANTI_FRAUD, LoanFlowMqConstant.Queue.LOAN_ANTI_FRAUD_QUEUE);
                log.info(methodNameFin + "-初始化借款反欺诈流程以及发送MQ结果:{}", isSuccess);
            } catch (InterruptedException ie) {
                log.error(methodNameFin + "-初始化借款反欺诈流程以及发送MQ异常，异常信息:{}", ie);
            }

        } catch (Exception e) {
            log.error(methodName + "-异常:{}", e);
            updateOrFailEndAndReturnProject(flowExecuteId, projectId, currentExecuteTimes, e.getMessage());
        }

    }

    /**
     * 跟进当前执行次数，更新标的申请流程执行表或者结束整个流程并退回标的
     *
     * @param flowExecuteId       借款流程控制ID
     * @param projectId           标的ID
     * @param currentExecuteTimes 当前执行次数
     * @param desc                描述(成功、失败原因)
     */
    private void updateOrFailEndAndReturnProject(Long flowExecuteId, Long projectId, int currentExecuteTimes, String desc) {
        if (currentExecuteTimes > EXECUTE_TIMES_THRESHOLD) {
            failEndProjectFlowExecuteAndReturnProject(flowExecuteId, projectId, ReturnProjectReasonTypeEnum.OTHER_EXCEPTION, desc);
        } else {
            projectFlowExecuteService.updateProjectFlowExecuteRuntime(flowExecuteId, LoanNodeTypeEnum.RISK_INFO_HANDLE, ProjectFlowExecuteRuntimeNodeStatusEnum.EXCEPTION, desc);
        }

    }

    /**
     * 异常触发天秤数据分析
     *
     * @param flowExecuteId
     * @param userId
     * @param mobileNo
     * @param projectType
     * @param fromSource
     */
    private void tianChengDataAnalysis(Long flowExecuteId, String userId, String mobileNo, Byte projectType, Byte fromSource) {

        String methodName = "12项风险信息规则处理, flowExecuteId:" + flowExecuteId + ", userId:" + userId + ", mobileNo:" + mobileNo;
        try {
            CompletableFuture.runAsync(() -> tianChengDataAnalysisDubboService.contactsAnalysis(userId, mobileNo, getDataAnalysisFromSource(projectType, fromSource)));
            log.info(methodName + "-通讯录分析调用完成");
        } catch (Exception e) {
            log.error(methodName + "-通讯录分析调用失败", e);
        }

        try {
            CompletableFuture.runAsync(() -> tianChengDataAnalysisDubboService.callRecordAnalysis(userId, mobileNo, getDataAnalysisFromSource(projectType, fromSource)));
            log.info(methodName + "-通话记录分析调用完成");
        } catch (Exception e) {
            log.error(methodName + "-通话记录分析调用失败", e);
        }

        try {
            CompletableFuture.runAsync(() -> tianChengDataAnalysisDubboService.smsAnalysis(userId, mobileNo));
            log.info(methodName + "-短信分析调用完成");
        } catch (Exception e) {
            log.error(methodName + "-短信分析调用失败", e);
        }

    }


    /**
     * 返回数据分析fromSource
     *
     * @param projectType 标的类型
     * @param fromSource  标的来源
     * @return 数据分析fromSource, 请参考com.niiwoo.civet.snapdata.enums.ProjectTypeRelationEnum
     */
    private String getDataAnalysisFromSource(Byte projectType, Byte fromSource) {

        if (fromSource != null) {
            if (ProjectFromSourceEnum.R360.getType() == fromSource.byteValue()) {
                return ProjectTypeRelationEnum.R360.getFromSource();
            }

            if (ProjectFromSourceEnum.CARD_NIU.getType() == fromSource.byteValue()) {
                return ProjectTypeRelationEnum.CARDNIU.getFromSource();
            }
        }

        if (projectType != null) {
            if (ProjectTypeEnum.SMALL_LOAN.getValue() == projectType.byteValue()) {
                return ProjectTypeRelationEnum.SPEEDLOAD.getFromSource();
            }

            if (ProjectTypeEnum.SOCIAL_SECURITY_LOAN.getValue() == projectType.byteValue()) {
                return ProjectTypeRelationEnum.SPEEDLOAD.getFromSource();
            }

            if (ProjectTypeEnum.CREDIT_CARD_LOAN.getValue() == projectType.byteValue()) {
                return ProjectTypeRelationEnum.SPEEDLOAD.getFromSource();
            }
            
            if (ProjectTypeEnum.CONSUME_LOAN.getValue() == projectType.byteValue()) {
                return ProjectTypeRelationEnum.SPEEDLOAD.getFromSource();
            }            
        }
        return ProjectTypeRelationEnum.SPEEDLOAD.getFromSource();
    }

    /**
     * 12项风险规则处理结束流程并且退回标的
     *
     * @param flowExecuteId
     * @param projectId
     * @param reasonTypeEnum
     * @param errMsg
     * @return
     */
    @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
    public boolean failEndProjectFlowExecuteAndReturnProject(Long flowExecuteId, Long projectId, ReturnProjectReasonTypeEnum reasonTypeEnum, String errMsg) {
        boolean result = failEndProjectFlowExecute(flowExecuteId, errMsg);
        if (!HintManagerHolder.isMasterRouteOnly()) {
            HintManagerHolder.clear();
            HintManager hintManager = HintManager.getInstance();
            hintManager.setMasterRouteOnly();
        }
        Project project = projectMapperExt.selectByPrimaryKey(projectId);
        if (project != null && project.getFromSource() != null && ProjectFromSourceEnum.HUIJIE.getType() == project.getFromSource().byteValue()) {
            ProjectFlowRequestDTO projectFlowRequestDTO = new ProjectFlowRequestDTO();
            projectFlowRequestDTO.setProjectId(projectId);
            projectFlowRequestDTO.setSubStatus(ProjectSubStatusEnum.MISCARRY_CANCER_BORROWER);
            projectDubboService.flow(projectFlowRequestDTO);
            //TODO GWP 流标通知汇介asset端
        } else {
            returnProject(projectId, reasonTypeEnum);
        }
        return result;
    }

    public boolean failEndProjectFlowExecute(Long flowExecuteId, String errMsg) {
        return projectFlowExecuteService.failEndProjectFlowExecute(flowExecuteId, LoanNodeTypeEnum.RISK_INFO_HANDLE, errMsg);
    }

    public void returnProject(Long projectId, ReturnProjectReasonTypeEnum reasonTypeEnum) {
        loanReturnService.returnProject(projectId, ProjectSubStatusEnum.CHECKING_BACK_PHONEOPERATOR_TIMEOUT, reasonTypeEnum);
    }

}
